// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:ffigen/src/code_generator/imports.dart';
import 'package:ffigen/src/config_provider/config.dart';
import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:ffigen/src/header_parser.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';

import '../test_utils.dart';

void main() {
  group('large_test', () {
    setUpAll(() {
      logWarnings(Level.SEVERE);
    });
    test('Libclang test', () {
      final includeDir = path.join(
        packagePathForTests,
        'third_party',
        'libclang',
        'include',
      );
      final logArr = <String>[];
      final logger = logToArray(logArr, Level.SEVERE);
      final config = FfiGen(
        logger,
        wrapperName: 'LibClang',
        wrapperDocComment: 'Bindings to LibClang.',
        output: Uri.file('unused'),
        compilerOpts: [...defaultCompilerOpts(Logger.root), '-I$includeDir'],
        commentType: CommentType(CommentStyle.doxygen, CommentLength.brief),
        entryPoints: [
          Uri.file(
            path.join(
              packagePathForTests,
              'third_party',
              'libclang',
              'include',
              'clang-c',
              'Index.h',
            ),
          ),
        ],
        shouldIncludeHeaderFunc: (Uri header) => [
          'BuildSystem.h',
          'CXCompilationDatabase.h',
          'CXErrorCode.h',
          'CXString.h',
          'Documentation.h',
          'FataErrorHandler.h',
          'Index.h',
        ].any((filename) => header.pathSegments.last == filename),
        functionDecl: DeclarationFilters.includeAll,
        structDecl: DeclarationFilters.includeAll,
        enumClassDecl: DeclarationFilters.includeAll,
        macroDecl: DeclarationFilters.includeAll,
        typedefs: DeclarationFilters.includeAll,
        typedefTypeMappings: [
          ImportedType(ffiImport, 'Int64', 'int', 'time_t'),
        ],
        preamble: '''
// ignore_for_file: camel_case_types, non_constant_identifier_names
''',
      );
      final library = parse(testContext(config));

      matchLibraryWithExpected(
        library,
        'large_test_libclang.dart',
        ['test', 'large_integration_tests', '_expected_libclang_bindings.dart'],
        // Remove comments containing @ to hack around a mismatch in the
        // documentation generated by different clang versions.
        codeNormalizer: (code) =>
            code.replaceAll(RegExp('[^\n]*///[^\n]*@[^\n]*\n'), ''),
      );

      const expectedEnumWarnings = [
        'CXAvailabilityKind',
        'CXCallingConv',
        'CXChildVisitResult',
        'CXCompletionChunkKind',
        'CXCursorKind',
        'CXDiagnosticSeverity',
        'CXErrorCode',
        'CXEvalResultKind',
        'CXIdxAttrKind',
        'CXIdxEntityCXXTemplateKind',
        'CXIdxEntityKind',
        'CXIdxEntityLanguage',
        'CXIdxEntityRefKind',
        'CXIdxObjCContainerKind',
        'CXLanguageKind',
        'CXLinkageKind',
        'CXLoadDiag_Error',
        'CXPrintingPolicyProperty',
        'CXRefQualifierKind',
        'CXResult',
        'CXSymbolRole',
        'CXTLSKind',
        'CXTUResourceUsageKind',
        'CXTemplateArgumentKind',
        'CXTokenKind',
        'CXTypeKind',
        'CXTypeNullabilityKind',
        'CXVisibilityKind',
        'CXVisitorResult',
        'CX_CXXAccessSpecifier',
        'CX_StorageClass',
      ];
      final actualLogs = logArr.join('\n');
      for (final e in expectedEnumWarnings) {
        expect(actualLogs, contains(e));
      }
    });

    test('CJSON test', () {
      final config = FfiGen(
        Logger.root,
        wrapperName: 'CJson',
        wrapperDocComment: 'Bindings to Cjson.',
        output: Uri.file('unused'),
        entryPoints: [
          Uri.file(
            path.join(
              packagePathForTests,
              'third_party',
              'cjson_library',
              'cJSON.h',
            ),
          ),
        ],
        shouldIncludeHeaderFunc: (Uri header) =>
            header.pathSegments.last == 'cJSON.h',
        functionDecl: DeclarationFilters.includeAll,
        structDecl: DeclarationFilters.includeAll,
        macroDecl: DeclarationFilters.includeAll,
        typedefs: DeclarationFilters.includeAll,
        preamble: '''
// ignore_for_file: camel_case_types, non_constant_identifier_names
''',
      );
      final library = parse(testContext(config));

      matchLibraryWithExpected(library, 'large_test_cjson.dart', [
        'test',
        'large_integration_tests',
        '_expected_cjson_bindings.dart',
      ]);
    });

    test('SQLite test', () {
      // Excluding functions that use 'va_list' because it can either be a
      // Pointer<__va_list_tag> or int depending on the OS.
      final config = FfiGen(
        Logger.root,
        wrapperName: 'SQLite',
        wrapperDocComment: 'Bindings to SQLite.',
        output: Uri.file('unused'),
        commentType: CommentType(CommentStyle.any, CommentLength.full),
        entryPoints: [
          Uri.file(
            path.join(
              packagePathForTests,
              'third_party',
              'sqlite',
              'sqlite3.h',
            ),
          ),
        ],
        shouldIncludeHeaderFunc: (Uri header) =>
            header.pathSegments.last == 'sqlite3.h',
        functionDecl: DeclarationFilters(
          shouldInclude: (declaration) => !{
            'sqlite3_vmprintf',
            'sqlite3_vsnprintf',
            'sqlite3_str_vappendf',
          }.contains(declaration.originalName),
        ),
        structDecl: DeclarationFilters.includeAll,
        globals: DeclarationFilters.includeAll,
        macroDecl: DeclarationFilters.includeAll,
        typedefs: DeclarationFilters.includeAll,
        preamble: '''
// ignore_for_file: camel_case_types, non_constant_identifier_names
''',
      );
      final library = parse(testContext(config));

      matchLibraryWithExpected(library, 'large_test_sqlite.dart', [
        'test',
        'large_integration_tests',
        '_expected_sqlite_bindings.dart',
      ]);
    });
  });
}
